package com.itzg.vboxfarm.service;

import java.util.Collection;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.Lifecycle;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.itzg.vboxfarm.domain.HostMachine;
import com.itzg.vboxfarm.domain.Site;
import com.itzg.vboxfarm.service.bits.HostChangedEvent;

@Service("hostConfigService")
@Transactional
public class HostConfigServiceImpl implements HostConfigService, ApplicationEventPublisherAware, Lifecycle {
	
	Logger logger = LoggerFactory.getLogger(HostConfigServiceImpl.class);
	
	@Autowired
	Environment env;

	@PersistenceContext
	EntityManager em;
	
	@Autowired
	HostConnectionResolver hostConnectionResolver;

	private ApplicationEventPublisher evtPublisher;

	private boolean running;

	@Override
	@SuppressWarnings("unchecked")
	@Transactional(readOnly=true)
	public Collection<Site> getAllSites() {
		return em.createQuery("SELECT s FROM Site s ORDER BY s.name")
				.getResultList();
	}
	
	@Override
	public void addSite(Site site) {
		logger.info("Persisting site {}", site);
		em.persist(site);
		em.flush();
	}
	
	@Override
	public Site findSite(long id) {
		return em.find(Site.class, id);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	@Transactional(readOnly=true)
	public Collection<HostMachine> getAllHosts() {
		return em.createQuery("FROM HostMachine")
				.getResultList();
	}
	
	@Override
	@SuppressWarnings("unchecked")
	@Transactional(readOnly=true)
	public Collection<HostMachine> getHostsForSite(Site site) {
		return em.createQuery("SELECT h FROM HostMachine h WHERE h.site = :site ORDER BY h.hostname")
				.setParameter("site", site)
				.getResultList();
	}
	
	@Override
	public void addHost(HostMachine hostMachine) {
		logger.debug("Add host {}, publishing event", hostMachine);
		evtPublisher.publishEvent(new HostChangedEvent(this, hostMachine, HostChangedEvent.Type.ADDED));
		em.persist(hostMachine);
	}
	
	@Override
	public void updateHost(HostMachine hostMachine) {
		logger.debug("Updating host {}", hostMachine);
		em.merge(hostMachine);
	}
	
	@Override
	public HostMachine findHost(long id) {
		return em.find(HostMachine.class, id);
	}
	
	@Override
	public void checkHost(long hostId) {
		HostMachine hostMachine = findHost(hostId);
		if (hostMachine != null) {
			hostConnectionResolver.resolve(hostMachine);
		}
	}
	
	@Override
	public HostMachine removeHost(long hostId) {
		HostMachine hostMachine = findHost(hostId);
		if (hostMachine != null) {
			logger.debug("Removing host {}", hostMachine);
			em.remove(hostMachine);
		}
		return hostMachine;
	}

	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher evtPublisher) {
		this.evtPublisher = evtPublisher;
	}

	@Override
	public void publishEvent(HostChangedEvent hostChangedEvent) {
		// Need this delegate method since prototype bean can't be publisher aware
		evtPublisher.publishEvent(hostChangedEvent);
	}
	
	@Override
	public void start() {
		logger.debug("Starting");
		Long count = (Long) em.createQuery("SELECT COUNT(*) FROM Site")
			.getSingleResult();
		
		if (count == 0) {
			logger.debug("No sites, so creating defalt one");
			Site site = new Site("Default");
			em.persist(site);
			
			if (env.acceptsProfiles("embedded")) {
				HostMachine hostMachine = new HostMachine("localhost");
				hostMachine.setSite(site);
				hostMachine.setWsUsername("vbox");
				hostMachine.setWsPassword("vboxpw");
				em.persist(hostMachine);
			}
		}
		running = true;
	}

	@Override
	public boolean isRunning() {
		return running;
	}

	@Override
	public void stop() {
		running = false;
	}
}
